
// 建造者模式

#include <iostream>
#include <string>
#include <memory>

// 抽象电脑类
class computer
{
public:
    void set_board(const std::string& board)
    {
        _board = board;
    }

    void set_display(const std::string& display)
    {
        _display = display;
    }

    virtual void set_os() = 0;

    void print_parameter()
    {
        std::cout << "\tboard:" << _board << std::endl;
        std::cout << "\tdisplay:" << _display << std::endl;
        std::cout << "\tos:" << _os << std::endl;
    }

protected:
    std::string _board;
    std::string _display;
    std::string _os;
};

// 具体产品类
class diy_example : public computer
{
public:
    void set_os() override
    {
        _os = "gun/linux";
    }
};

// 抽象建造者类
// 包含创建一个产品各个零部件的接口
class builder
{
public:
    virtual void build_board(const std::string& board) = 0;
    virtual void build_display(const std::string& display) = 0;
    virtual void build_os() = 0;
    virtual std::shared_ptr<computer> build() = 0;
};

// 具体产品的具体建造类
class diy_example_builder : public builder
{
public:
    diy_example_builder()
    :_pc(new diy_example())
    {}

    void build_board(const std::string& board) override
    {
        _pc->set_board(board);
    }

    void build_display(const std::string& display) override
    {
        _pc->set_display(display);
    }

    void build_os() override
    {
        _pc->set_os();
    }

    std::shared_ptr<computer> build() override
    {
        return _pc;
    }

private:
    std::shared_ptr<computer> _pc;
};

// 指挥者类
class director
{
public:
    director(builder *b)
    :_pb(b)
    {}

    void construct(const std::string& board, const std::string& display)
    {
        _pb->build_board(board);
        _pb->build_display(display);
        _pb->build_os();
    }

private:
    std::shared_ptr<builder> _pb;
};

int main() 
{
    builder *b = new diy_example_builder();
    std::unique_ptr<director> pd(new director(b));
    pd->construct("华硕主板", "三星显示器");
    std::shared_ptr<computer> pc = b->build();
    pc->print_parameter();

    return 0;
}